#pragma once

#include "AsyncLibDef.h"

namespace AsyncLib
{

class CTaskThread;
class CThreadpoolWork;
class CThreadpoolTimer;
class CThreadpoolWait;
class CTpTimerManager;
class CTpFileManager;

class CAsyncLib : public IAsyncLib
{
public:
	CAsyncLib();
	~CAsyncLib();

	void Init(ATID atidMax) override;
	void Exit(bool bWaitTasks) override;

	void PostTask(ATID atid, std::function<void()>&& fnTask) override;
	void PostDelayedTask(UINT nMilliseconds, std::function<void()>&& fnTask) override;
	void WaitForAsync(HANDLE handle, UINT nMilliseconds, std::function<void(DWORD)>&& fnCallback) override;

	bool IsInTaskThread(ATID atid) const override;
	bool IsInThreadPool() const override;
	void SetInTaskThread(ATID atid);
	void SetInThreadPool();

	bool IsRunning() const { return m_status == Running; }
	auto GetCallbackEnv() { return &m_tpCallbackEnv; }
	ATID InnerGetAtid() const;
	void InnerPostTask(ATID atid, std::function<void()>&& fnTask);

	std::shared_ptr<ITimerTask> CreateTimerTask(bool bUITimer) override;
	std::shared_ptr<IFileTask> CreateFileTask(HANDLE hFile) override;

private:
	void WaitForAllTasks();
	void InitTaskThreads(ATID atidMax);
	void ExitTaskThreads();
	void InitThreadpools();
	void ExitThreadpools();
	void ProcessCallbacks();

	volatile enum { Exited, Exiting, Running } m_status;
	TP_CALLBACK_ENVIRON m_tpCallbackEnv;
	PTP_POOL          m_pTpPool;
	PTP_CLEANUP_GROUP m_pTpCleanupGroup;
	std::shared_mutex m_mutex;
	std::deque<std::function<void()>> m_deqTasks; // Save callbacks on exit that need to switch threads
	std::vector<std::unique_ptr<CTaskThread>> m_vecTaskThreads;
	std::unique_ptr<CThreadpoolWork>  m_pThreadpoolWork;
	std::unique_ptr<CThreadpoolTimer> m_pThreadpoolTimer;
	std::unique_ptr<CThreadpoolWait>  m_pThreadpoolWait;
	std::shared_ptr<CTpTimerManager>  m_pTpTimerManager;
	std::shared_ptr<CTpFileManager>   m_pTpFileManager;
};

extern CAsyncLib g_asyncLib;

} // namespace AsyncLib
